#include "ogrebindingPCH.h"
#include "entitybinding.h"
#include "vector3binding.h"
#include "quaternionbinding.h"

namespace lua_ogre {

const char* EntityClassName = "Entity";

LuaEntity::LuaEntity(Ogre::Entity *entity) : c_obj(entity)
{
	if (NULL != entity)
	{
		entity->setListener(this);
	}
}

LuaEntity::~LuaEntity()
{}

const char* LuaEntity::GetName() const
{
	if (NULL == c_obj)
	{
		return "Invalid Entity";
	}

	return c_obj->getName().c_str();
}

void LuaEntity::objectDestroyed(Ogre::MovableObject*)
{
	if (c_obj != NULL)
	{
		c_obj->setListener(NULL);
		c_obj = NULL;
	}
}

int LuaCreateEntity(Ogre::Entity *entity, lua_State *ls)
{
	void *mem = lua_newuserdata(ls, sizeof(LuaEntity));
	if (NULL == mem)
	{
		return 0;
	}
	new(mem) LuaEntity(entity);
	luaL_getmetatable(ls, EntityClassName);
	lua_setmetatable(ls, -2);
	return 1;
}

static int GetPosition(Ogre::Entity *self, lua_State *ls)
{
	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't get entity position which doesn't have parent node!";
		return 0;
	}
	return LuaCreateVector3(self->getParentNode()->getPosition(), ls);
}

static int SetPosition(Ogre::Entity *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, Vector3ClassName))
	{
		LogLuaError << "invalid value for Entity.position!";
		return 0;
	}

	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't get set position which doesn't have parent node!";
		return 0;
	}

	self->getParentNode()->setPosition(*(Ogre::Vector3*)lua_touserdata(ls, 1));
	return 0;
}

static int GetOrient(Ogre::Entity *self, lua_State *ls)
{
	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't get entity orientation which doesn't have parent node!";
		return 0;
	}
	return LuaCreateQuaternion(self->getParentNode()->getOrientation(), ls);
}

static int SetOrient(Ogre::Entity *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, QuaternionClassName))
	{
		LogLuaError << "invalid value for Entity.orientation!";
		return 0;
	}
	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't set entity orientation which doesn't have parent node!";
		return 0;
	}
	self->getParentNode()->setOrientation(*(Ogre::Quaternion*)lua_touserdata(ls, 1));
	return 0;
}

static int Rotate(Ogre::Entity *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, QuaternionClassName))
	{
		LogLuaError << "invalid value for Entity.orientation!";
		return 0;
	}
	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't set entity orientation which doesn't have parent node!";
		return 0;
	}
	self->getParentNode()->rotate(*(Ogre::Quaternion*)lua_touserdata(ls, 1));
	return 0;
}

static int SetScale(Ogre::Entity *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, Vector3ClassName))
	{
		LogLuaError << "invalid value type for entity.scale";
		return 0;
	}
	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't set entity scale which doesn't have parent node!";
		return 0;
	}
	self->getParentNode()->setScale(*(Ogre::Vector3*)lua_touserdata(ls, 1));
	return 0;
}

static int GetScale(Ogre::Entity *self, lua_State *ls)
{
	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't get entity scale which doesn't have parent node!";
		return 0;
	}
	return LuaCreateVector3(self->getParentNode()->getScale(), ls);
}

static int GetBounding(Ogre::Entity *self, lua_State *ls)
{
    const Ogre::AxisAlignedBox &aabb = self->getBoundingBox();
    LuaCreateVector3(aabb.getCenter(), ls);
    LuaCreateVector3(aabb.getHalfSize(), ls);
    return 2;
}
    
    static int PlayAnimation(Ogre::Entity *self, lua_State *ls)
    {
        if (!lua_isstring(ls, 1)) {
            LogLuaError << "invalid parameter";
            return 0;
        }
        
        const char *name = lua_tostring(ls, 1);
        Ogre::AnimationState *as = self->getAnimationState(name);
        if (!as) {
            LogLuaError << "animation " << name << " not found!";
            return 0;
        }
        
        bool loop = false;
        if (lua_isboolean(ls, 2)) {
            loop = lua_toboolean(ls, 2);
        }
        
        as->setEnabled(true);
        as->setLoop(loop);
        
        return 0;
    }
    
    static int UpdateAnimation(Ogre::Entity *self, lua_State *ls)
    {
        if (!lua_isstring(ls, 1) || !lua_isnumber(ls, 2)) {
            LogLuaError << "invalid parameter";
            return 0;
        }
        
        const char *name = lua_tostring(ls, 1);
        Ogre::AnimationState *as = self->getAnimationState(name);
        if (!as) {
            LogLuaError << "animation " << name << " not found";
            return 0;
        }
        as->addTime(lua_tonumber(ls, 2));
        return 0;
    }

static LuaMethod<Ogre::Entity> g_entity_methods_and_props[] = 
{
	{"position", 0, GetPosition, SetPosition},
	{"orientation", 0, GetOrient, SetOrient},
	{"scale", 0, GetScale, SetScale},
	{"rotate", Rotate, 0, 0},
    {"GetBounding", GetBounding, 0, 0},
    {"PlayAnimation", PlayAnimation, 0, 0},
    {"UpdateAnimation", UpdateAnimation, 0, 0},
	{0, 0, 0, 0}
};

static int EntityAlloc(lua_State *ls)
{
	LogLuaError << "call Scene:createentity() please!";
	return 0;
}

void BindEntity(lua_State *ls)
{
	typedef LuaClassBind<Ogre::Entity, LuaEntity> EntityBind;

	LuaTypeObject<Ogre::Entity> type_obj(
		EntityClassName,
		g_entity_methods_and_props, 
		EntityAlloc,
		EntityBind::Dealloc,
		EntityBind::ToString,
		EntityBind::Thunk);

	EntityBind::Register(ls, "ov", type_obj);
}

}